/** @file gui.cpp
    @brief The minimalistic GUI */

#include "gui/gui.h"

// GUI singletons
G3::GUI::GUI G3::GUI::MainGui;

//==============================================================================
// Constructors
//==============================================================================
G3::GUI::GUI::GUI () {
    ActiveWidget = NULL;
}

//==============================================================================
// Destructors
//==============================================================================
G3::GUI::GUI::~GUI () {
    pLabels.clear ();
    pGroups.clear ();
    pButtons.clear ();
    pUnknown.clear ();
}

//==============================================================================
// GUI Key Down event handler
//==============================================================================
void G3::GUI::OnKeyDown (G3::INPUT *Input, SDL_keysym* keysym, float aElapsedTime) {
    if (MainGui.ActiveWidget == NULL)
        return;

    Assert (Input, "G3::GUI_OnKeyDown: Input argument is a NULL pointer..\n");
    Assert (keysym, "G3::GUI_OnKeyDown: Key Symbol argument is a NULL pointer..\n");

    EDITBOX *eBox = NULL;

    if (MainGui.ActiveWidget->GetType () == MainTypeMgr.GetType ("W_EDITBOX")) {
        // Cut out unnecessary keys for an editbox
        if (keysym->sym >= SDLK_SPACE && keysym->sym <= SDLK_DELETE ||
            keysym->sym >= SDLK_BACKSPACE && keysym->sym <= SDLK_RETURN ||
            keysym->sym >= SDLK_KP0 && keysym->sym <= SDLK_PAGEDOWN) {

            eBox = (EDITBOX *) MainGui.ActiveWidget;

            eBox->LastKey = keysym->sym;
            eBox->LastModifier = Input->Keyboard.Modifiers;
            // Stop repeating
            eBox->RepeatingAKey = false;
            // Restart the timer
            eBox->ProcCharInput (keysym->sym, Input->Keyboard.Modifiers);
        }
    }
}

//==============================================================================
// GUI Key Up event handler
//==============================================================================
void G3::GUI::OnKeyUp (G3::INPUT *Input, SDL_keysym* keysym, float aElapsedTime) {
    if (MainGui.ActiveWidget == NULL)
        return;

    Assert (Input, "G3::GUI::OnKeyUp: Input argument is a NULL pointer..\n");
    Assert (keysym, "G3::GUI::OnKeyUp: Key Symbol argument is a NULL pointer..\n");

    EDITBOX *eBox = NULL;

    if (MainGui.ActiveWidget->GetType () == MainTypeMgr.GetType ("W_EDITBOX")) {
        eBox = (EDITBOX *) MainGui.ActiveWidget;

        eBox->LastKey = -1;
        eBox->LastModifier = Input->Keyboard.Modifiers;
        // Stop repeating
        eBox->RepeatingAKey = false;
        // Restart the timer
        KeyDelayTimer.Start ();
    }
}

//==============================================================================
// GUI Mouse Move event handler
//==============================================================================
void G3::GUI::OnMouseMove (G3::INPUT *Input, SDL_Event* Event, float aElapsedTime) {
    Assert (Input, "G3::GUI::OnMouseMove: Input argument is a NULL pointer..\n");

    MainGui.Root.CheckMouse (Input, WIDGET::W_OVER);

    for (int i=0; i<(int)MainGui.pScrollbars.size (); i++) {
        if (MainGui.pScrollbars [i])
            MainGui.pScrollbars [i]->SigMouseMove (Input, MainGui.pScrollbars [i]);
    }
}

//==============================================================================
// GUI Mouse Down event handler
//==============================================================================
void G3::GUI::OnMouseDown (G3::INPUT *Input, SDL_Event* Event, float aElapsedTime) {
    Assert (Input, "G3::GUI::OnMouseDown: Input argument is a NULL pointer..\n");

    MainGui.ActiveWidget = MainGui.Root.CheckMouse (Input, WIDGET::W_DOWN);
}

//==============================================================================
// GUI Mouse Up event handler
//==============================================================================
void G3::GUI::OnMouseUp (G3::INPUT *Input, SDL_Event* Event, float aElapsedTime) {
    Assert (Input, "G3::GUI::OnMouseUp: Input argument is a NULL pointer..\n");

    MainGui.Root.CheckMouse (Input, WIDGET::W_UP);

    for (int i=0; i<(int)MainGui.pScrollbars.size (); i++) {
        if (MainGui.pScrollbars [i])
            MainGui.pScrollbars [i]->SigMouseUp (Input, MainGui.pScrollbars [i]);
    }
}

//==============================================================================
// Attaches an input device to the GUI
//==============================================================================
void G3::GUI::GUI::AttachInput (G3::INPUT *aInput) {
    Assert (aInput, "G3::GUI::AttachInput: Input argument is a NULL pointer..\n");

    aInput->doOnKeyDown (OnKeyDown);
    aInput->doOnKeyUp (OnKeyUp);
    aInput->doOnMouseMove (OnMouseMove);
    aInput->doOnMouseDown (OnMouseDown);
    aInput->doOnMouseUp (OnMouseUp);
}

//==============================================================================
// Adds a widget to the gui
//==============================================================================
G3::GUI::WIDGET *G3::GUI::GUI::AddWidget (WIDGET *aParent, WIDGET *aWidget) {
    Assert (aWidget, "GUI::AddWidget: Widget argument is a NULL pointer");

    WIDGET *parent = aParent;

    if (aParent == NULL)
        parent = &Root;

    parent->Children.push_back (aWidget);

    parent->ExpandByChildren ();

    // Make the just added widget active
    ActiveWidget = aWidget;

    if (aWidget->GetType () == MainTypeMgr.GetType ("W_LABEL"))
        pLabels.push_back ((LABEL *) aWidget);
    else if (aWidget->GetType () == MainTypeMgr.GetType ("W_GROUP"))
        pGroups.push_back ((GROUP *) aWidget);
    else if (aWidget->GetType () == MainTypeMgr.GetType ("W_BUTTON"))
        pButtons.push_back ((BUTTON *) aWidget);
    else if (aWidget->GetType () == MainTypeMgr.GetType ("W_EDITBOX"))
        pEditBoxes.push_back ((EDITBOX *) aWidget);
    else if (aWidget->GetType () == MainTypeMgr.GetType ("W_SCROLLBAR"))
        pScrollbars.push_back ((SCROLLBAR *) aWidget);
    else
        pUnknown.push_back (aWidget);

    return aWidget;
}

//==============================================================================
// Sets focus on a widget
//==============================================================================
void G3::GUI::GUI::SetFocus (WIDGET *widget) {
    ActiveWidget = widget;
}

//==============================================================================
// Draws the whole gui
//==============================================================================
void G3::GUI::GUI::Draw (float aElapsedTime) {
    int i = 0;

    // Get the viewport coordinates
    int ViewPort [4];
    glGetIntegerv (GL_VIEWPORT, ViewPort);

    bool Lighting = false;

    if (glIsEnabled (GL_LIGHTING)) {
        glDisable (GL_LIGHTING);
        Lighting = true;
    }

    glDisable (GL_DEPTH_TEST);
    glSetPlanar (G3::RECTI (ViewPort [0], ViewPort [3], ViewPort [2], ViewPort [1]), -1.0f, 1.0f);
    glTranslated (ViewPort [0], ViewPort [3], 0);

    for (i=0; i<(int) pLabels.size (); i++) {
        Assert (pLabels [i], "GUI::Draw: Invalid Label pointer");
        pLabels [i]->Draw (aElapsedTime);
    }

    for (i=0; i<(int) pEditBoxes.size (); i++) {
        Assert (pEditBoxes [i], "GUI::Draw: Invalid Editbox pointer");
        pEditBoxes [i]->Draw (aElapsedTime);
    }

    for (i=0; i<(int) pGroups.size (); i++) {
        Assert (pGroups [i], "GUI::Draw: Invalid Group pointer");
        pGroups [i]->Draw (aElapsedTime);
    }

    for (i=0; i<(int) pButtons.size (); i++) {
        Assert (pButtons [i], "GUI::Draw: Invalid Button pointer");
        pButtons [i]->Draw (aElapsedTime);
    }

    for (i=0; i<(int) pScrollbars.size (); i++) {
        Assert (pScrollbars [i], "GUI::Draw: Invalid Scrollbar pointer");
        pScrollbars [i]->Draw (aElapsedTime);
    }

    for (i=0; i<(int) pUnknown.size (); i++) {
        Assert (pUnknown [i], "GUI::Draw: Invalid Unknown pointer");
        pUnknown [i]->Draw (aElapsedTime);
    }
    glRestorePerspective ();
    glEnable (GL_DEPTH_TEST);

    if (Lighting)
        glEnable (GL_LIGHTING);
}

